/******************************************************************************
 * Author:      671643387
 * Created:     2016/9/30
 *****************************************************************************/

var mUtil = require('./util.js');
var ErrorCodes = require('./error.js').ErrorCodes;
var MahjongPlayer = require('./player.js').Player;
var mFunc = require('./function.js');
var subGame = require('./sub/index.js');
var mjMark = require('./mjmark.js');

///////////////////////////////////////////////////////////////////////////////

/**
 * 麻将牌
 * @constructor
 */
function Mahjongs(owner) {
    this.mahjongs = [];
    this.owner = owner;
}
Mahjongs.prototype = {
    /**
     * 生成麻将
     * @param category
     */
    gen: function(category) {
        this.mahjongs = mUtil.genMahjong(category);
    },

    /**
     * 摸牌
     * @param room
     * @param num
     * @returns {Array}
     */
    dealCards: function(num) {
        var room = this.owner;

        var cards = [];
        for (var i = 0; i < num; ++i) {
            cards.push(this.mahjongs[room.roundInfo.curDealIndex]);
            room.roundInfo.curDealIndex += 1;
            if (room.roundInfo.curDealIndex >= 108) {
                room.roundInfo.curDealIndex = 0;
            }
        }
        return cards;
    },

    /**
     * 获取剩余多少张牌
     * @returns {number}
     */
    getCardNum: function() {
        var room = this.owner;

        if (room.roundInfo.curDealIndex > room.roundInfo.dealIndex) {
            return this.mahjongs.length - room.roundInfo.curDealIndex + room.roundInfo.dealIndex;
        } else {
            return room.roundInfo.dealIndex - room.roundInfo.curDealIndex;
        }
    }
};

/**
 * 已经出的牌
 * @constructor
 */
function DealedMahjons(owner) {
    this.mahjongs = [];
    this.owner = owner;
}
DealedMahjons.prototype = {
    add: function(card, owner) {
        card.owner = owner;
        this.mahjongs.push(card);
    },

    find: function (owner, Card) {
        var numCard = 0;
        this.mahjongs.forEach(function (card) {
            if(card.owner == owner && card.value == Card){
                numCard += 1;
            }
        });
        return numCard;
    },

    // 设置刚打出的这张牌的标记(1/2/3:碰/杠/胡)
    setCurCardFlag: function(flag) {
        this.mahjongs[this.mahjongs.length - 1].flag = flag;
    },

    getSyncData: function() {
        return this.mahjongs;
    },

    getPlayedCards: function(uid, all) {
        all = all || true;

        var cards = [];
        this.mahjongs.forEach(function(card){
            if (card.owner == uid) {
                var cMark = card.flag || 0;
                if (all) {
                    cards.push(card.value);
                } else if (cMark == 0) {
                    cards.push(card.value);
                }
            }
        });
        return cards;
    },

    clear: function() {
        this.mahjongs = [];
    },
};

/**
 * 麻将房间
 * @param roomId        房间号
 * @param creatorId     创建者编号
 * @param args          参数
 * @constructor
 */
function Room(roomId, creatorId, args) {
    this.id = roomId;                   // 房间编号
    this.creator = creatorId;           // 创建者编号

    this.game = {};
    this.subGame = null;                // 子逻辑

    this.state = ROOM_STATE_WAITING;    // 房间状态
    this.newStates = [];                // 房间要切换到的状态
    this.stateSwitching = false;        // 房间正在切换

    this.mahjongs = new Mahjongs(this);     // 桌面牌

    this.roundInfo = {                  // 当前轮游戏数据
        round           : 0,            // 当前是第几轮
        dice1           : 0,            // 骰子1的值
        dice2           : 0,            // 骰子2的值
        dealIndex       : 0,            // 发牌索引
        curDealIndex    : 0,            // 摸牌索引

        dealer          : 1,            // 庄家
        curPlay         : 1,            // 当前出牌玩家
        isRoomHuPai     : false,        // 当前局是否有人胡牌

        canHuNum        : 0,            // 当前能和牌玩家数量
        huPlayers       : [],           // 本轮胡牌玩家UIDs
        dianPaoUid      : 0,            // 点炮玩家UID

        curMahjong      : {},           // 当前出的牌
        dealedMahjons   : new DealedMahjons(this),  // 已经出的牌
    };

    this.players = {                    // 玩家
        1 : new MahjongPlayer(this),
        2 : new MahjongPlayer(this),
        3 : new MahjongPlayer(this),
        4 : new MahjongPlayer(this),
    };
    this.joinedPlayers = 0;             // 当前房间人数
    this.readyPlayers = 0;              // 准备就绪的人数
    this.disconnectedPlayerNum = false; // 掉线人数
    this.isDestroyed = false;           // 房间是否被销毁

    //>> 申请解散房间
    this.isReqForDestroy = false;       // 正在请求销毁
    this.acceptDestroyPlayers = [];     // 确认销毁的玩家
    this.destroyTime = 0;               // 自动销毁时间
    this.isSureToDestroy = false;       // 确认房间销毁

    // 初始化数据
    this.init(args);
};


Room.prototype = {

    init: function(args) {
        this.game = {                                        // 游戏信息
            type: args.type,                                 // 类型
            multiple: args.multiple || 1,                    // 番数
            baseScore: args.baseScore || 1,                  // 底分
            totalRound: args.totalRound || 1,           // 总轮数
        };

        this.subGame = createSubGame(this.game.type, this);
        if (!this.subGame) {
            throw "NO SUCH SUB GAME";
        }
    },

    isFull : function () {
        return this.joinedPlayers == 4;
    },
    isDestroy: function() {
        return this.isDestroyed;
    },

    isPlayerInRoom: function(uid) {
        for (var pos in this.players) {
            if (!this.players.hasOwnProperty(pos)) {
                continue;
            }

            if (this.players[pos].isPlayer(uid)) {
                return true;
            }
        }

        return false;
    },

    enumerationPlayer: function(handler) {
        for (var key in this.players) {
            if (!this.players.hasOwnProperty(key)) {
                continue;
            }

            if (handler(+key, this.players[key]) === false) {
                break;
            }
        }
    },

    getPlayer: function(uid) {
        for (var key in this.players) {
            if (!this.players.hasOwnProperty(key)) {
                continue;
            }

            var player = this.players[key];
            if (player.isPlayer(uid)) {
                return +key;
            }
        }

        return 0;
    },

    findEmptyPos: function() {
        for (var key in this.players) {
            if (!this.players.hasOwnProperty(key)) {
                continue;
            }

            if (!this.players[key].isInited()) {
                return +key;
            }
        }
        return 0;
    },

    findRoomCunPlayerHandr: function () {
        var curPlay = this.roundInfo.curPlay;
        if(this.players[curPlay].isInited()){
            return this.players[curPlay];
        }
        return 0;
    },

    onPlayerDisconnect: function(player) {
        // 有人掉线
        this.disconnectedPlayerNum += 1;

        var playerIndex = this.getPlayer(player.uid);

        // 给其他玩家推送通知
        this.enumerationPlayer(function(ePlayerIdx, ePlayer){
            if (ePlayer.isPlayer(player.uid) || !ePlayer.isInited()) {
                return;
            }

            ePlayer.sendPacket('setPlayerOffline', {
                playerIndex: playerIndex
            });
        }.bind(this));
    },

    destroy: function(uid, callback) {
        if (this.creator != uid) {
            callback(ErrorCodes.ERROR_PLAYER_NOT_CREATOR);
            return;
        }

        // 房主只能在没有游戏时销毁房间
        if (this.state == ROOM_STATE_PLAYING || this.state == ROOM_STATE_SETTLEMEN) {
            callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
            return;
        }

        // 判断需不需要保存战报
        if (this.state == ROOM_STATE_PLAYING || this.state == ROOM_STATE_SETTLEMEN) {
            this.onRoomFinish();
        }

        this.onDestroy();
        callback();
    },
    onDestroy: function() {
        // 广播房间被销毁通知
        this.boradcastMessage('setRoomDestroy', {});

        // 标记房间已经销毁
        this.isDestroyed = true;

        var playerUids = [];
        this.enumerationPlayer(function (ePlayerIdx, ePlayer) {
            if (ePlayer.isInited()) {
                playerUids.push(ePlayer.uid);
            }
        });

        // 通知游戏服务器该房间已经销毁
        requestGame(this.creator, {
            playerUids: playerUids,
        }, 'mahjong', 'roomDestroyed', function(resp){
            DEBUG(resp.desc);
        });
    },

    /**
     * 将玩家加入房间
     * @param conn          // 连接对象
     * @param uid           // 玩家UID
     * @param player        // 玩家数据
     * @param callback
     */
    addPlayer: function(conn, uid, player, callback) {
        // 检查房间状态
        //if (this.state !=  ROOM_STATE_WAITING) {
        //    callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
        //    return;
        //}

        // 判断玩家是否已经在房间里面
        var mPlayerIdx = this.getPlayer(uid);
        if (mPlayerIdx != 0) {
            var mPlayer = this.players[mPlayerIdx];
            if (mPlayer.isOnline()) {
                callback(ErrorCodes.ERROR_PLAYER_ALREADY_IN_ROOM);
                return;
            } else {
                // 玩家断线重连
                mPlayer.init(conn, uid, player);


                // 同步数据
                var syncData = null;
                if (this.state == ROOM_STATE_SETTLEMEN) {
                    // 房间正在结算切玩家已经就绪
                    syncData = {
                        totalRound: this.game.totalRound,             // 游戏总轮数
                        round: this.roundInfo.round,                  // 游戏当前轮数
                        dealer: this.roundInfo.dealer,                // 当前庄家
                        curPlay: this.roundInfo.curPlay,              // 当前出牌的人
                        curMahjong: 0,  // 当前出的牌
                        remainCardNum: 0,    // 剩余牌数量
                        playerIndex: mPlayerIdx,                      // 玩家位置
                        players: {},
                    };
                    var dealedMahjongs = this.roundInfo.dealedMahjons;
                    this.enumerationPlayer(function(ePlayerIdx, ePlayer){
                        var userInfo = ePlayer.getSyncData(true);
                        syncData.players[+ePlayerIdx] = userInfo;

                        // 过滤数据
                        if (!ePlayer.isPlayer(uid)) {
                            userInfo.cards = [];
                            userInfo.card = {};
                        }

                        // 玩家已经出的牌
                        userInfo.dealedMahjons = [];
                    });
                } else {
                    syncData = {
                        totalRound: this.game.totalRound,             // 游戏总轮数
                        round: this.roundInfo.round,                  // 游戏当前轮数
                        dealer: this.roundInfo.dealer,                // 当前庄家
                        curPlay: this.roundInfo.curPlay,              // 当前出牌的人
                        curMahjong: this.roundInfo.curMahjong.value,  // 当前出的牌
                        remainCardNum: this.mahjongs.getCardNum(),    // 剩余牌数量
                        playerIndex: mPlayerIdx,                      // 玩家位置
                        players: {},
                    };
                    var dealedMahjongs = this.roundInfo.dealedMahjons;
                    this.enumerationPlayer(function(ePlayerIdx, ePlayer){
                        var userInfo = ePlayer.getSyncData();
                        syncData.players[+ePlayerIdx] = userInfo;

                        // 过滤数据
                        if (!ePlayer.isPlayer(uid)) {
                            userInfo.cards = [];
                            userInfo.card = {};
                        }

                        // 玩家已经出的牌
                        userInfo.dealedMahjons = dealedMahjongs.getPlayedCards(ePlayer.uid);
                    });
                }
                mPlayer.sendPacket('setPlayerReconnectInfo', syncData);
                mPlayer.onReconnect();


                // 广播玩家上线消息
                this.boradcastMessage('setPlayerOnline', {
                    playerIndex: mPlayerIdx
                });

                // 修改房间状态
                this.disconnectedPlayerNum -= 1;
                callback();
                return;
            }
        }

        // 找到空闲位置
        var playerIndex = this.findEmptyPos();
        if (playerIndex == 0) {
            callback(ErrorCodes.ERROR_INTERNAL_ERROR);
            return;
        }

        // 将这个玩家关联到房间
        var newPlayer = this.players[playerIndex];
        newPlayer.init(conn, uid, player);
        this.joinedPlayers += 1;
        this.readyPlayers += 1;

        LOG(util.format('PLAYER %s JOINED ROOM %d - %d', player.name, this.id, Date.getStamp()));

        // 广播有人加入房间消息
        var newPlayerInfo = newPlayer.getInfo();
        this.boradcastMessage('setNewPlayer', {
            playerIndex: playerIndex,
            playerInfo: newPlayerInfo,
        }, [playerIndex]);

        this.subGame.onPlayerJoinedRoom(playerIndex, newPlayer);

        // 将房间的一些状态数据发给玩家
        var roomInfo = {
            //round: this.roundInfo.round,
            //dealer: this.roundInfo.dealer,
            totalRound: this.game.totalRound,

            players: {},
        };
        for (var pos in this.players) {
            if (!this.players.hasOwnProperty(pos)) {
                continue;
            }
            roomInfo.players[+pos] = this.players[pos].getInfo();
        }
        callback(null, roomInfo);

        // 检查房间
        LOG("Room.addPlayer " + this.joinedPlayers);
        if (this.joinedPlayers == 4) {
            // 房间就绪，可以开始游戏


            this.pushNewState(ROOM_STATE_READY);
        }
    },

    baoTing: function (uid, card, callback) {
        if (this.state != ROOM_STATE_PLAYING) {
            callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
            return;
        }

        // 检查玩家是否在本房间
        var playerIndex = this.getPlayer(uid);
        if (playerIndex == 0) {
            callback(ErrorCodes.ERROR_PLAYER_NOT_IN_ROOM);
        }
        var curPlayer = this.players[playerIndex];


        // 检查是否该这个玩家出牌
        if (this.roundInfo.curPlay != playerIndex) {
            callback(ErrorCodes.ERROR_NOT_PLAYER_ROUND);
            return;
        }

        // 玩家杠牌
        if (curPlayer.baoTing()) {
            // 发送玩家报听消息
            this.boradcastMessage('setPlayerMark', {
                mark: mjMark.PLAY_MARK_BAO_TING,
                playerIndex: playerIndex
            });
        }
        callback();
    },

    playCard: function(uid, card, callback) {
        if (this.state != ROOM_STATE_PLAYING) {
            callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
            return;
        }

        // 检查玩家是否在本房间
        var playerIndex = this.getPlayer(uid);
        if (playerIndex == 0) {
            callback(ErrorCodes.ERROR_PLAYER_NOT_IN_ROOM);
        }
        var curPlayer = this.players[playerIndex];


        // 检查是否该这个玩家出牌
        if (this.roundInfo.curPlay != playerIndex) {
            callback(ErrorCodes.ERROR_NOT_PLAYER_ROUND);
            return;
        }

        // 这个人出牌
        if (!curPlayer.playCard({value: card})) {
            callback(ErrorCodes.ERROR_PLAYER_PLAY_CARD_FAILED);
            return;
        }
        //这个玩家报听了，但是打的不是刚摸起来的牌
        if(curPlayer.baoting && curPlayer.newMahjong.value != card){
            callback(ErrorCodes.ERROR_PLAYER_PLAY_CARD_ERR);
            return;
        }

        // 设置出牌信息
        this.roundInfo.curMahjong.value = card;
        this.roundInfo.dealedMahjons.add({value: card}, uid);

        // 广播出牌消息
        this.boradcastMessage('setPlayedCard', {
            card: card,
            playerIndex: playerIndex,
        });

        ///////////////////////////////////////////////////////////////////////
        // 检查其他几个玩家
        var canPlayerDoAction = false;
        this.enumerationPlayer(function(ePlayerIdx, ePlayer){
            if (ePlayer.isPlayer(uid)) {
                return;
            }

            if (ePlayer.isHu()) {
                return;
            }

            ePlayer.checkWithCurMahjong(this.roundInfo.curMahjong, curPlayer);
            canPlayerDoAction = canPlayerDoAction || ePlayer.hasAnyTip();
            if (ePlayer.hasTip('hu')) {
                this.roundInfo.canHuNum += 1;
            }
        }.bind(this));

        if (canPlayerDoAction) {
            // 其他玩家可以碰牌或者杠牌
            if (this.roundInfo.canHuNum > 0) {
                // 给可以胡牌的玩家发提示
                this.enumerationPlayer(function(ePlayerIdx, ePlayer){
                    if (ePlayerIdx == playerIndex || ePlayer.isHu()) {
                        return;
                    }

                    if (ePlayer.hasTip('hu')) {
                        ePlayer.sendTips(this.roundInfo.canHuNum != 1);
                    }
                }.bind(this));
            } else {
                // 给可以杠牌或者碰牌的玩家发提示
                this.enumerationPlayer(function(ePlayerIdx, ePlayer){
                    if (ePlayerIdx == playerIndex || ePlayer.isHu()) {
                        return;
                    }
                    ePlayer.sendTips();
                });
            }
        } else {
            // 其他玩家啥都不能干
            this.subGame.onPlayerPlayedCardAndNoOtherAction(curPlayer, this.roundInfo.curMahjong, playerIndex);
            this.sendPlayCardPlayer(0, true);
        }
        callback();
    },

    gangCard: function(uid, callback) {
        if (this.state != ROOM_STATE_PLAYING) {
            callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
            return;
        }

        // 检查玩家是否在本房间
        var playerIndex = this.getPlayer(uid);
        if (playerIndex == 0) {
            callback(ErrorCodes.ERROR_PLAYER_NOT_IN_ROOM);
        }
        var curPlayer = this.players[playerIndex];

        // 玩家杠牌
        var playerGangCard = curPlayer.tips.gang;
        var code = curPlayer.gangCard(this.roundInfo.curMahjong);
        if (code == 0) {
            // 该牌被玩家杠了
            if (this.roundInfo.curMahjong.value != 0) {
                var coverPengPlayer = this.players[this.roundInfo.curPlay];

                this.subGame.onPlayerGangAction(curPlayer, this.roundInfo.curMahjong, coverPengPlayer);

                if(curPlayer.newMahjong.value == 0) {
                    //被点杠
                    coverPengPlayer.markPush(mjMark.PLAY_MARK_PASSIVE_GANG, this.roundInfo.curMahjong.value);
                }
                this.roundInfo.dealedMahjons.setCurCardFlag(2);
                this.roundInfo.curMahjong.value = 0;
            }

            // 发送玩家杠牌消息
            this.boradcastMessage('setPlayerGangCard', {
                playerIndex: playerIndex,
                card: playerGangCard,
                mark: curPlayer.getLastMark(),
            });

            // 发送出牌消息
            this.sendPlayCardPlayer(playerIndex, true);
            callback();
        } else {
            callback(code);
        }
    },

    pengCard: function (uid, callback) {
        if (this.state != ROOM_STATE_PLAYING) {
            callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
            return;
        }

        // 检查玩家是否在本房间
        var playerIndex = this.getPlayer(uid);
        if (playerIndex == 0) {
            callback(ErrorCodes.ERROR_PLAYER_NOT_IN_ROOM);
        }
        var curPlayer = this.players[playerIndex];

        // 玩家碰牌
        var playerPengCard = curPlayer.tips.peng;
        var code = curPlayer.pengCard(this.roundInfo.curMahjong);
        if (code == 0) {
            this.subGame.onPlayerPengAction(curPlayer, this.roundInfo.curMahjong, this.players[this.roundInfo.curPlay]);

            this.boradcastMessage('setPlayerPengCard', {
                playerIndex: playerIndex,
                card: playerPengCard,
            });

            // 该牌被玩家碰了
            this.roundInfo.dealedMahjons.setCurCardFlag(1);
            this.roundInfo.curMahjong.value = 0;

            // 碰牌后只出牌不摸牌
            this.sendPlayCardPlayer(playerIndex, false);

            callback();
        } else {
            callback(code);
        }
    },

    // 发牌并通知客户端该谁出牌
    sendPlayCardPlayer: function(pos, dealCard) {
        if (dealCard && this.mahjongs.getCardNum() == 0) {
            //当前局结束
            this.setRoomSettlementState();
            return;
        }

        pos = pos || (this.roundInfo.curPlay + 1);
        this.roundInfo.curPlay = pos;
        if (this.roundInfo.curPlay > this.joinedPlayers) {
            this.roundInfo.curPlay = 1;
        }

        this.boradcastMessage('setPlayCardPlayer', {
            playerIndex: this.roundInfo.curPlay
        });

        if (dealCard) {
            var curPlayer = this.players[this.roundInfo.curPlay];
            curPlayer.setNewMahjong(this.mahjongs.dealCards(1)[0]);
            if (curPlayer.hasTip('hu')) {
                this.roundInfo.canHuNum += 1;
            }

            this.boradcastMessage('setRemainCards', {
                remainCards: this.mahjongs.getCardNum()
            });
        }
    },

    huCard: function (uid, callback) {
        if (this.state != ROOM_STATE_PLAYING) {
            callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
            return;
        }

        // 检查玩家是否在本房间
        var playerIndex = this.getPlayer(uid);
        if (playerIndex == 0) {
            callback(ErrorCodes.ERROR_PLAYER_NOT_IN_ROOM);
        }
        var curPlayer = this.players[playerIndex];

        // 玩家胡牌
        var code = curPlayer.huCard(this.roundInfo.curMahjong);
        if (code == 0) {
            // 该牌被玩家胡了
            this.roundInfo.isRoomHuPai = true;
            this.roundInfo.canHuNum -= 1;
            this.roundInfo.huPlayers.push(uid);

            this.subGame.onCunPlayerPlayCardAction();

            var coverHuPaiPlayer = this.players[this.roundInfo.curPlay];

            // 如果是，点炮、热炮，给当时出牌玩家加点炮、热炮标志
            if(curPlayer.hasHuPaiMethod(mjMark.HUCARD_METHOD_DIANPAO) && !coverHuPaiPlayer.hasHuPaiMethod(mjMark.HUCARD_METHOD_DIANPAO)){
                // 设置点炮玩家
                if (!coverHuPaiPlayer.isHu()) {
                    this.roundInfo.dianPaoUid = coverHuPaiPlayer.uid;
                }
                if(coverHuPaiPlayer.getBaoTing()){
                    curPlayer.setKillBao();
                }
                coverHuPaiPlayer.pushHuPaiMethod(mjMark.HUCARD_METHOD_DIANPAO);
            }else if(curPlayer.hasHuPaiMethod(mjMark.HUCARD_METHOD_REPAO) && !coverHuPaiPlayer.hasHuPaiMethod(mjMark.HUCARD_METHOD_REPAO)){
                // 设置点炮玩家
                if (!coverHuPaiPlayer.isHu()) {
                    this.roundInfo.dianPaoUid = coverHuPaiPlayer.uid;
                }
                if(coverHuPaiPlayer.getBaoTing()){
                    curPlayer.setKillBao();
                }
                coverHuPaiPlayer.pushHuPaiMethod(mjMark.HUCARD_METHOD_REPAO);
            }

            if (this.roundInfo.canHuNum == 0) {
                // 能糊的人都糊了
                if (this.roundInfo.curMahjong.value != 0) {
                    this.roundInfo.dealedMahjons.setCurCardFlag(3);
                }
                this.roundInfo.curMahjong.value = 0;

                if (this.subGame.canGameContinueWhenPlayerHuCard()) {
                    // 有人胡牌后游戏还能继续
                    // TODO
                } else {
                    // 有人胡牌后游戏不能继续,计算当前局
                    this.setRoomSettlementState();
                }
            } else {
                // 等待其他可以胡牌的玩家胡牌或者跳过
            }

            // 广播有人胡牌的消息
            this.boradcastMessage('setPlayerHuPai', {
                playerIndex: playerIndex
            });

            callback();
        } else {
            callback(code);
        }

    },

    pass: function(uid, callback) {
        if (this.state != ROOM_STATE_PLAYING) {
            callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
            return;
        }

        // 检查玩家是否在本房间
        var playerIndex = this.getPlayer(uid);
        if (playerIndex == 0) {
            callback(ErrorCodes.ERROR_PLAYER_NOT_IN_ROOM);
        }
        var curPlayer = this.players[playerIndex];

        // 可以和牌的玩家选择过
        if (curPlayer.hasTip('hu')) {
            curPlayer.clearTips();

            curPlayer.isErrorHu = true;

            this.roundInfo.canHuNum -= 1;
            if (this.roundInfo.canHuNum == 0) {

                if (this.roundInfo.isRoomHuPai && !this.subGame.canGameContinueWhenPlayerHuCard()) {
                    // 房间有人和牌且游戏不能继续，结算当前局
                    this.setRoomSettlementState();
                } else {
                    // 继续游戏
                    var doAnyAction = false;    // 是否给其他玩家发送提示,如果有发送等待其他玩家操作

                    this.enumerationPlayer(function(playerIndex, ePlayer){
                        if (ePlayer.isPlayer(uid) || ePlayer.isHu()) {
                            return;
                        }

                        if (ePlayer.hasAnyTip()) {
                            ePlayer.sendTips();
                            doAnyAction = true;
                        }
                    }.bind(this));

                    if (!doAnyAction) {
                        //点过的玩家不是自摸，下一个玩家摸牌
                        if(curPlayer.hasHuPaiMethod(mjMark.HUCARD_METHOD_ZIMO)) {
                            callback && callback();
                            return;
                        }
                        this.subGame.onPlayerPassGangOrPengAction(curPlayer);
                        // 没有给其他玩家发送提示
                        this.sendPlayCardPlayer(0, true);
                    }
                }
            }
        } else {
            this.subGame.onPlayerPassGangOrPengAction(curPlayer);
            curPlayer.clearTips();
            this.sendPlayCardPlayer(0, true);
        }
    },

    continue: function(uid, callback) {
        if (this.state != ROOM_STATE_SETTLEMEN) {
            callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
            return;
        }

        // 检查玩家是否在本房间
        var playerIndex = this.getPlayer(uid);
        if (playerIndex == 0) {
            callback(ErrorCodes.ERROR_PLAYER_NOT_IN_ROOM);
        }
        var curPlayer = this.players[playerIndex];

        // 设置点炮玩家
        if (!curPlayer.isHu() && (curPlayer.hasHuPaiMethod(mjMark.HUCARD_METHOD_DIANPAO) || curPlayer.hasHuPaiMethod(mjMark.HUCARD_METHOD_REPAO))) {
            this.roundInfo.dianPaoUid = uid;
        }

        curPlayer.continue();
        // 广播房间就绪
        this.boradcastMessage('setPlayerReady', {
            playerIndex: playerIndex,
        });

        this.readyPlayers += 1;
        if (this.readyPlayers == this.joinedPlayers) {
            // 开始下一局游戏
            if (this.roundInfo.huPlayers.length == 1) {
                // 只有1人胡牌，设置胡牌玩家为庄家
                this.roundInfo.dealer = this.getPlayer(this.roundInfo.huPlayers[0]);
            } else if(this.roundInfo.dianPaoUid != 0){
                // 设置点炮玩家为庄家
                this.roundInfo.dealer = this.getPlayer(this.roundInfo.dianPaoUid);
            }
            this.pushNewState(ROOM_STATE_PLAYING);
        }
        callback();
    },

    sendTipMsg: function(uid, msgId, msg, callback) {
        //if (this.state != ROOM_STATE_SETTLEMEN) {
        //    callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
        //    return;
        //}

        // 检查玩家是否在本房间
        var playerIndex = this.getPlayer(uid);
        if (playerIndex == 0) {
            callback(ErrorCodes.ERROR_PLAYER_NOT_IN_ROOM);
        }

        this.boradcastMessage('setTipMsg', {
            playerIndex: playerIndex,
            msgId: msgId,
            msg: msg,
        }, [playerIndex]);
    },

    update: function() {
        // 房间已经销毁
        if (this.isDestroyed) {
            return true;
        }

        // 正在切换状态
        if (this.stateSwitching) {
            return;
        }

        // 倒计时销毁房间
        if (this.isReqForDestroy && (Date.getStamp() >= this.destroyTime)) {
            this.isSureToDestroy = true;
            this.pushNewState(ROOM_STATE_SETTLEMEN);
        }

        // 切换状态
        if (this.newStates.length != 0) {
            var toState = this.newStates.splice(0, 1);
            this.switchState(toState[0]);
        }
    },

    /**
     * 结算当前局
     */
    settlementCurrentGame: function () {
        this.readyPlayers = 0;
        this.subGame.onSettlementCurrentGame();

        // 所有轮结束或者房间已经确认要解散
        if ((this.game.totalRound == this.roundInfo.round) || this.isSureToDestroy) {
            this.onRoomFinish();
            this.onDestroy();
        }
    },

    onRoomFinish: function() {
        // 通知游戏服务器该房间已经结束
        var playersInfo = {};
        this.enumerationPlayer(function (ePlayerIdx, ePlayer) {
            playersInfo[ePlayer.uid] = {
                integral: ePlayer.integral
            };
        });

        requestGame(this.creator, {
            roomId: this.id,
            time: Date.getStamp(),
            players: playersInfo,
        }, 'mahjong', 'roomFinished', function(resp){
            DEBUG(resp.desc);
        });
    },

    reqDestroyRoom: function(uid, callback) {
        if (this.isReqForDestroy) {
            //callback(ErrorCodes.ERROR_ROOM_ALREADY_REQD_DESTROY);
            return;
        }

        //if (this.state != ROOM_STATE_PLAYING) {
        //    callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
        //    return;
        //}

        // 检查玩家是否在本房间
        var playerIndex = this.getPlayer(uid);
        if (playerIndex == 0) {
            callback(ErrorCodes.ERROR_PLAYER_NOT_IN_ROOM);
        }

        this.isReqForDestroy = true;                // 设置标记
        this.destroyTime = Date.getStamp() + 300;   // 自动销毁倒计时

        this.acceptDestroyPlayers = [];
        this.acceptDestroyPlayers.push(uid);

        this.boradcastMessage('setRoomReqForDestroy', {
            playerIndex: playerIndex,
            time: this.destroyTime,
        });

        callback();
    },

    respDestroyRoom: function(uid, ok, callback) {
        if (!this.isReqForDestroy) {
            callback(ErrorCodes.ERROR_ROOM_NOT_REQD_DESTROY);
            return;
        }

        //if (this.state != ROOM_STATE_PLAYING) {
        //    callback(ErrorCodes.ERROR_ROOM_STATE_ERROR);
        //    return;
        //}

        // 检查玩家是否在本房间
        var playerIndex = this.getPlayer(uid);
        if (playerIndex == 0) {
            callback(ErrorCodes.ERROR_PLAYER_NOT_IN_ROOM);
            return;
        }

        if (this.acceptDestroyPlayers.indexOf(uid) != -1) {
            callback(ErrorCodes.ERROR_PLAYER_ALREADY_ACCEPT);
            return;
        }

        if (!ok) {
            // 有玩家拒绝，取消销毁房间
            this.isReqForDestroy = false;
            this.destroyTime = 0;

            this.boradcastMessage('setPlayerRespRoomReqForDestroy', {
                playerIndex: playerIndex,
                ok: ok,
            });
        } else {
            // 该玩家同意销毁
            this.acceptDestroyPlayers.push(uid);

            if (this.acceptDestroyPlayers.length == (this.joinedPlayers - this.disconnectedPlayerNum)) {
                // 在线玩家都同意销毁，立即结算游戏
                this.isSureToDestroy = true;
                this.pushNewState(ROOM_STATE_SETTLEMEN);
            }
        }
    },

    ///////////////////////////////////////////////////////////////////////////
    //>> 功能函数

    // 广播消息
    boradcastMessage: function(act, msg, exclude) {
        exclude = exclude || [];
        for (var key in this.players) {
            if (!this.players.hasOwnProperty(key)) {
                continue;
            }

            if (exclude.indexOf(+key) != -1) {
                continue;
            }

            if (this.players[key].isInited()) {
                this.players[key].sendPacket(act, msg);
            }
        }
    },

    pushNewState: function(state) {
        this.newStates.push(state);
    },

    /**
     * 设置房间为结算状态
     * @param
     */
    setRoomSettlementState: function () {
        this.pushNewState(ROOM_STATE_SETTLEMEN);
    },

    // 房间状态切换
    switchState: function(newState) {
        this.stateSwitching = true;
        LOG('TRY SWITCH TO ' + newState);

        var _me = this;
        async.series([function(cb){
            // 切出当前状态
            cb();
        }, function(cb){
            // 切入新状态
            switch (newState) {
                case ROOM_STATE_READY:
                    // 直接跳到游戏开始
                    _me.pushNewState(ROOM_STATE_PLAYING);
                    break;
                case ROOM_STATE_PLAYING:
                    var roundInfo = _me.roundInfo;

                    // 增加轮数
                    roundInfo.round += 1;
                    // 重置数据
                    roundInfo.huPlayers = [];
                    roundInfo.isRoomHuPai = false;
                    roundInfo.curMahjong = { value: 0 };
                    roundInfo.dealedMahjons.clear();
                    roundInfo.curPlay = roundInfo.dealer;
                    roundInfo.dianPaoUid = 0;

                    _me.subGame.onInit(roundInfo.round == 1);

                    async.series([function(cb2){
                        // 发送房间信息
                        _me.boradcastMessage('setRoomInfo', {
                            round: _me.roundInfo.round,
                            dealer: _me.roundInfo.dealer,
                        });
                        cb2();
                    },function(cb2){
                        // 掷骰子
                        var dice1 = Math.floor(Math.random() * 6) + 1;
                        var dice2 = Math.floor(Math.random() * 6) + 1;

                        // 保存当前轮骰子值
                        roundInfo.dice1 = dice1;
                        roundInfo.dice2 = dice2;

                        cb2();
                    }, function(cb2){
                        // 发牌
                        var dice = roundInfo.dice1 + roundInfo.dice2;
                        var side = dice % 4;
                        var index = side * 36;
                        index += dice * 2;

                        if (index >= 108) {
                            index = Math.abs(108 - index);
                        }

                        roundInfo.dealIndex = index;         // 发牌索引
                        roundInfo.curDealIndex = index;      // 摸牌索引

                        // 给玩家发牌
                        var mahjongs = _me.mahjongs;
                        mahjongs.gen(_me.game.type);
                        _me.players[1].setCards(mahjongs.dealCards(13));
                        _me.players[2].setCards(mahjongs.dealCards(13));
                        _me.players[3].setCards(mahjongs.dealCards(13));
                        _me.players[4].setCards(mahjongs.dealCards(13));

                        _me.sendPlayCardPlayer(_me.roundInfo.dealer, true);

                        cb2();
                    }], function(err){
                        cb(err);
                    });
                    return;
                case ROOM_STATE_SETTLEMEN:
                    _me.settlementCurrentGame();
                    break;
                default:
                    break;
            }
            cb();
        }], function(err){
            _me.state = newState;
            LOG('SWITCHED TO ' + newState);

            _me.stateSwitching = false;
        });
    }
};
exports.Room = Room;

///////////////////////////////////////////////////////////////////////////////
//>> 房间状态
var ROOM_STATE_WAITING  = 1;        // 等待玩家加入
var ROOM_STATE_READY    = 2;        // 等待房主开始游戏
var ROOM_STATE_PLAYING  = 3;        // 游戏中
var ROOM_STATE_SETTLEMEN= 4;        // 游戏结算中


///////////////////////////////////////////////////////////////////////////////
//>> 功能函数

function createSubGame(type, owner) {
    switch (type) {
        case "guiyang":
            return new subGame[type].SubGame(owner);
    }
    return null;
}
